package org.jmlx.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 对线属性值对比
 *
 * @author: fulin
 * @since: 2024-08-02
 **/
@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanCompareUtil {

    /**
     * 全量比对两个对线
     *
     * @param first  来源对象
     * @param second 目标对象
     * @return 对比结果
     */
    public static List<FieldInfo> compareBean(Object first, Object second) {
        List<FieldInfo> fieldInfoList = CollUtil.newArrayList();
        handleCompare(first, second, fieldInfoList, first.getClass().getSimpleName());
        return fieldInfoList;
    }

    /**
     * 部分比对
     *
     * @param first    来源对象
     * @param second   目标对象
     * @param fileList 需要比对的字段
     * @return 对比结果
     */
    public static List<FieldInfo> compareBean(Object first, Object second, String... fileList) {
        List<FieldInfo> fieldInfoList = CollUtil.newArrayList();
        handleCompare(first, second, fieldInfoList, first.getClass().getSimpleName(), fileList);
        return fieldInfoList;
    }

    /**
     * 部分比对
     *
     * @param first   来源对象
     * @param second  目标对象
     * @param fileList 需要比对的字段
     * @return 对比结果
     */
    public static <T> List<FieldInfo> compareBean(T first, T second, Func1<T, ?>... fileList) {
        Assert.isTrue(ObjectUtil.isNotEmpty(fileList), "fileList 不能为空");
        String [] list = Arrays.stream(fileList).map(LambdaUtil::getFieldName).toArray(String[]::new);
        List<FieldInfo> fieldInfoList = CollUtil.newArrayList();
        handleCompare(first, second, fieldInfoList, first.getClass().getSimpleName(), list);
        return fieldInfoList;
    }

    private static void handleCompare(Object first, Object second, List<FieldInfo> fieldInfoList, String className, String... fileList) {
        if (ObjectUtil.isNull(first) || ObjectUtil.isNull(second)) {
            return;
        }
        Field[] declaredFields = first.getClass().getDeclaredFields();
        if (ObjectUtil.isNotEmpty(declaredFields) && ObjectUtil.isNotEmpty(fileList)) {
            declaredFields = Arrays.stream(declaredFields).filter(field -> Arrays.stream(fileList)
                    .distinct().anyMatch(file -> file.equals(field.getName()))).toArray(Field[]::new);
        }

        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            Class<?> type = declaredField.getType();
            Object fieldValue = BeanUtil.getFieldValue(first, declaredField.getName());
            Object fieldValue2 = BeanUtil.getFieldValue(second, declaredField.getName());
            String classNameStr = className + StrUtil.DASHED + declaredField.getName();
            if (!ClassUtils.isPrimitiveOrWrapper(type) && !type.equals(String.class)) {
                if (fieldValue instanceof List) {
                    List<Object> list = (List<Object>) fieldValue;
                    List<Object> list2 = (List<Object>) fieldValue2;
                    for (int i = 0; i < list.size(); i++) {
                        handleCompare(list.get(i), list2.get(i), fieldInfoList, classNameStr, fileList);
                    }
                } else if (fieldValue instanceof Map) {
                    Map<Object, Object> map = (Map<Object, Object>) fieldValue;
                    Map<Object, Object> map2 = (Map<Object, Object>) fieldValue2;
                    map.forEach((k, v) -> {
                        handleCompare(v, map2.get(k), fieldInfoList, classNameStr, fileList);
                    });
                } else {
                    handleCompare(fieldValue, fieldValue2, fieldInfoList, classNameStr, fileList);
                }
            } else {
                Object property = BeanUtil.getProperty(first, declaredField.getName());
                Object property2 = BeanUtil.getProperty(second, declaredField.getName());
                if (ObjectUtil.notEqual(property, property2)) {
                    log.info("字段: {},旧值:{},新值:{}", classNameStr, property, property2);
                    fieldInfoList.add(new FieldInfo(declaredField.getName(), property, property2));
                }
            }
        }
    }


}

